iT邦幫忙

2021 iThome 鐵人賽

DAY 17
0
自我挑戰組

馬克的軟體架構小筆記系列 第 17

30-17 之 DataSource Layer - Active Record

  • 分享至 

  • xImage
  •  

接下來要這篇文章要來談談很常聽到的『 Active Record 』。

什麼是 Active Record ?

根據 《 Patterns of Enterprise Application Architecture - Martin Fowler 》書中的定義如下 :

An object carries both data and behavior.

對,基本上就是在 DataSource 層裡,然後每個實體代表一個 table row,且實體內包含資料與行為 ( domain 邏輯 ) ,這和上一章的 RowDataGateway 很像,都是它缺了行為。下圖就是書中的範例物件 UML 圖。沒看錯。

https://ithelp.ithome.com.tw/upload/images/20211002/20089358mhKDPOUmN6.png
圖片來源: 《 Patterns of Enterprise Application Architecture - Martin Fowler 》

是不是所有的 domain 邏輯都會丟在 Active Record 呢 ? 那要在什麼時後用呢 ?

我覺得不是,我覺得作者本意不是,不然為什麼還要有 Domain 層呢 ? 並且還將 Active Record 放在 DataSource 層,我覺得他應該是指將一些簡單的 domain logic 放在 active record 中,例如下述範例中的 isPass。

class StudentTest{
	id: string
	stuentId: string
	score: number

	isPass(): boolean{
     return score >= 60;
  }
}

作者只建議在以下的情境下使用 :

  • 業務邏輯簡單,並且與 Table 相近。
  • 如果 domain 層是使用 Transaction Script 模式,就是一個不錯的選擇。

並且他還要說到一句話,它事實上沒有以上的情況不要用 :

Another argument against Active Record is the fact that it couples the object design to the database design. This makes it more difficult to refactor either design as a project goes forward

簡單的說就是會讓物件設計與資料庫設計綁在一起。

一些常見語言有使用 Active Record 的地方

目前我常聽到的,基本上有使用 Active Record Pattern 來實作的有以下幾個 :

書中我覺得怪怪的地方

還記不記得上一章節的 RowDataGateway,它每個實體都代表資料庫中的一行,而 query 因為有時會回傳多個,所以他拉出一個叫『 Finder 』的東東,但在 Active Record 範例中我到沒看到這個東西呢… 那他多筆資料的 query 也是寫在 Active Record 嗎 ?

然後看一下實作的 framework 例如 Laravel ,看起來就是丟在裡面。

$flight = App\Flight::where('active', 1)->first();
$flights = App\Flight::find([1, 2, 3]);

戰爭

事實上有不說少了在抱怨這個模式,請參閱以下的文章。

範例

這個範例與 RowDataGateway 相同,都是以 Person 為範例,然後這裡有個重點要注意一下 :

這個地方 Active Record 的類別,我是以 Model 為命名,主要的原因是不少實作 Active Record 的語言,都是以 Model 為命名。

所以我現在暫時的將我對 Model 這個詞的定義為 :

以 Active Record 為概念的類別稱為 Model

https://ithelp.ithome.com.tw/upload/images/20211002/20089358QLRWMxXnnl.png

// DataSouree Layer
const mockDbData = [
    {
        id: 1,
        username: 'mark',
        age: 19,
        company: 'hahow'
    },
    {
        id: 2,
        username: 'ian',
        age: 19,
        company: 'amazon'
    }
]
function executeSql(sql: string){
    return mockDbData
}

class PersonModel{
    id: number 
    username: string
    age: number
    company: string
    constructor(id: string,username: string, age: number, company: string)
    constructor(username: string, age: number, company: string)
    update(): void{
        console.log(`UPDATE person SET username=${this.username}, age=${this.age}, company=${this.company} WHERE id=${this.id}`)
    }
    insert(): void{
        console.log(`INSERT INTO person (username, age, company) VALUES(${this.username}, ${this.age}, ${this.company})`)
    }
    // Domain Logical
    isAdult(): boolean{
        return this.age >= 18
    }
		// Domain Logical
    isVIP(): boolean{
        return ['HAHOW','GOOGLE','KKBOX'].includes(this.company)
    }

    static findByCompany(company: string): PersonModel[]{
        const result: PersonModel[] = []
        const sql = `SELECT * FROM person WHERE company=${company}`
        const resultSet = executeSql(sql)
        for (const data of resultSet) {
            result.push(new PersonModel(data.id, data,username, data.age, data.company))
        }
        return result 
    }
}
// Domain Layer
class PersonTableModule{
    register(username: string, age: number , company: string){
        // 假設有個業務需求為公司有限定人數 limit = 10
        const personsInCompany: PersonModel[] = PersonModel.findByCompany(company) 
        if(personsInCompany.length >= 10) throw Error('公司人數已達限制')

        const person: PersonModel = new PersonModel(username, age, company)
        if(!person.isAdult()) throw Error('要成年才能註冊喔')
        person.insert()
    }
}

小總結

這個知識點可以用來解釋什麼現象

這個模式基本上就是以 row 為單位,來進行資料操作與業務,這個在簡單的業務專案使用真的快速放便,但是我覺得這應該是架構師最難抓的,要如何判斷未來會不會複雜,會不會 overdesign,不知道有沒有什麼標準呢 ?

這個知識點可以和以前的什麼知識連結呢 ?

首先來談談 Domain Model,它是一個 Domain Layer 的模式,一個實體就是一筆資料,然後他裡面有很多業務邏輯與資料,只是他與 Active Record 的差別我覺得在於,Domain Model 應該是沒有限定一個 table,而是以 domain 為主,這我個人的看法 ~ 不一定對。

而 RowDataGateway 就是一個實體代表一個 row,但與 Active Record 不同處在於,它裡面沒有業務邏輯,而且好像有讀寫分離,整理上我個人是比較喜歡 RowDataGateway。

最後還有一個連結,那就是 ORM,ORM 本質上是與資料庫的對應實體,並且應該是不包含業務邏輯,所以兩者應該是不相同,但我覺得在 Active Record 中可以使用 ORM。

我要如何運用這個知識點 ?

  • 現在看到 Active Record 終於可以明確知道它的定義囉 ~ 這樣在看一些文章時,總算可以判斷對方寫的有沒有問題。
  • Active Record 我個人是比較不建議用,但我可以反思,為什麼當初會有它產生呢 ? 彈性、快速 ? 我覺得也有可能是那個時代的軟體開發模式說不定就是以 Table 來開始發想 Domain 呢,我猜的。

參考資料


上一篇
30-16 之 DataSource Layer - RowDataGateway
下一篇
30-18 之 DataSource Layer- DataMapper
系列文
馬克的軟體架構小筆記29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
西撒
iT邦新手 5 級 ‧ 2021-10-23 12:14:41

Domain Model 應該是沒有限定一個 table,而是以 domain 為主,這我個人的看法 ~ 不一定對

ddd 相關書籍也是這樣說的
domain model 以 aggregate 作為 修改狀態的最小邊界
與 repo (data layer) 互動

一個 aggregate 可能是多張 table 所組合起來的

讀取的話, 因為不會修改系統的狀態
可以忽略 domain model
根據不同需求, 定義不同的 read model

馬克 iT邦研究生 3 級 ‧ 2021-10-25 18:42:00 檢舉

嗯 ~ 我自已也是這樣理解 ~ 只是 PoEAAA 那本有時總是沒說的很明確 ……

我要留言

立即登入留言